// 日志格式化输出类:通过用户给出的日志格式化字符串(或者使用的是我们的缺省值)去构建一个日志信息
#pragma once
#include "message.hpp"
#include "level.hpp"
#include "util.hpp"
#include <memory>
#include <cassert>
#include <sstream>
#include <vector>

namespace wu
{
    // 格式化的基类，不同的格式化信息由各自的派生类去实现
    class FormatItem
    {
    public:
        using ptr = std::shared_ptr<FormatItem>;
        virtual void format(std::ostream &os, LogMsg msg) = 0;
    };

    // 派生格式化子项:信息 等级 时间 文件名 行号 日志器名称 线程ID 制表符 换行 其他
    class MsgFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, LogMsg msg) override
        {
            os << msg._payload;
        }
    };
    class LevelFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, LogMsg msg) override
        {
            os << LogLevel::toString(msg._level);
        }
    };
    class TimeFormatItem : public FormatItem
    {
    public:
        TimeFormatItem(const std::string &format_time = "%H:%M:%S")
            : _format_time(format_time)
        {
        }

        void format(std::ostream &os, LogMsg msg) override
        {
            // 先转化再进行格式时间的创建
            struct tm t;
            localtime_r(&msg._ctime, &t);
            char time[32];
            strftime(time, 31, _format_time.c_str(), &t);
            os << time;
        }

    private:
        std::string _format_time; // 我们的时间需要从时间戳的形式转化为指定的日期格式(时分秒)，这个格式可以由用户自己指定
        // 如果用户没有自定我们就在构造的时候给个缺省的日期转化格式
    };
    class FileFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, LogMsg msg) override
        {
            os << msg._file;
        }
    };
    class LineFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, LogMsg msg) override
        {
            os << msg._line;
        }
    };
    class LoggerFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, LogMsg msg) override
        {
            os << msg._logger;
        }
    };

    class ThreadFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, LogMsg msg) override
        {
            os << msg._tid;
        }
    };

    class TabFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, LogMsg msg) override
        {
            os << "\t";
        }
    };
    class NlineFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, LogMsg msg) override
        {
            os << "\n";
        }
    };
    // 原字符串
    class OtherFormatItem : public FormatItem
    {
    private:
        std::string _str;

    public:
        OtherFormatItem(const std::string &str = "")
            : _str(str)
        {
        }
        void format(std::ostream &os, LogMsg msg) override
        {
            os << _str;
        }
    };

    class Formatter
    {
        /*
            %d ⽇期
            %T 缩进
            %t 线程id
            %p ⽇志级别
            %c ⽇志器名称
            %f ⽂件名
            %l ⾏号
            %m ⽇志消息
            %n 换⾏
        */
    public:
        using ptr = std::shared_ptr<Formatter>;
        Formatter(const std::string &pattern = "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n")
            :_pattern(pattern)
        {
            assert(parsePattern());//格式化字符串必须解析成功
        }

        //直接进行打印日志
        void format(std::ostream& out, LogMsg &msg)
        {
            for(auto &it : _items)
            {
                it->format(out, msg);
            }
        }
        //将日志信息放入到字符流中，后续可以向文件或者数据库中进行写入
        std::string format(LogMsg &msg)
        {
            std::stringstream ss;
            for(auto &it : _items)
            {
                it->format(ss, msg);
            }
            return ss.str();
        }
    private:
        bool parsePattern()
        {
            std::vector<std::pair<std::string, std::string>> fmt_order;
            //1.对格式化规则字符串进行解析
            //2.将解析的结果放在fmt_order中
            //%cabcd[%d{...}][%p]%T%m%n
            size_t pos = 0;
            std::string key, val;
            while(pos < _pattern.size())
            {
                //先判断是否是%
                if(_pattern[pos] != '%')
                {
                    val.push_back(_pattern[pos++]);
                    continue;
                }
                //走到这里就说明遇到了%，这时我们还需要处理%%的情况  %%->%
                if(pos+1 < _pattern.size() && _pattern[pos+1] == '%')
                {
                    val.push_back('%');
                    pos += 2;//跳过两个%
                    continue;
                }
                //走到这里说明原字符串已经处理完毕了
                if(!val.empty())//加一个判断是因为如果第一个字符就是格式化的字符的话val的处理结果就是空,对于空串我们不进行push_back
                {
                    fmt_order.push_back(std::make_pair("", val));
                    val.clear();
                }

                pos += 1;//走到格式化字符位置
                if(pos == _pattern.size())
                {
                    std::cout << "%之后没有匹配的格式化字符" << std::endl;
                    return false;
                }
                key = _pattern[pos];//出现了格式化字符
                pos += 1;
                if(pos < _pattern.size() && _pattern[pos] == '{')
                {
                    //格式化字串出现了，比如  %d{%H:%M:%S} 
                    //这个时候我们就要将遇到}之前的所有字符全部组合成字串再push_back
                    pos += 1;//第一个格式化字符的位置
                    while(pos < _pattern.size() && _pattern[pos] != '}')
                    {
                        val.push_back(_pattern[pos++]);
                    }
                    if(pos == _pattern.size())
                    {
                        //说明没有匹配的}，字串是不完整的
                        std::cout << "子规则{}匹配出错" << std::endl;
                        return false;
                    }
                    pos += 1;//跳出字串的}
                }
                fmt_order.push_back(std::make_pair(key, val));
                key.clear();
                val.clear();
            }
            //3.根据解析得到的错误信息的格式化规则去填写_items
            for(auto &it : fmt_order)
            {
                _items.push_back(createItem(it.first, it.second));
            }
            return true;
        }
        
        FormatItem::ptr createItem(const std::string &key, const std::string &val)
        {
            if(key == "d") return std::make_shared<TimeFormatItem>(val);
            if(key == "t") return std::make_shared<ThreadFormatItem>();
            if(key == "c") return std::make_shared<LoggerFormatItem>();
            if(key == "f") return std::make_shared<FileFormatItem>();
            if(key == "l") return std::make_shared<LineFormatItem>();
            if(key == "p") return std::make_shared<LevelFormatItem>();
            if(key == "T") return std::make_shared<TabFormatItem>();
            if(key == "m") return std::make_shared<MsgFormatItem>();
            if(key == "n") return std::make_shared<NlineFormatItem>();
            if(key.empty()) return std::make_shared<OtherFormatItem>(val);
            //如果key不为空的话就说明有意料之外的格式化字符比如%g,这时我们应该提出错误,然后结束程序
            std::cout << "没有对应的格式化字符 %" << key << std::endl;
            //abort();//引发异常而退出
            exit(-1);//正常退出，释放资源
        }

    private:
        // 错误信息的格式化规则，可以是用户自定的，如果用户没有自定的话，我们就给一个缺省的错误信息格式化控制当成默认的
        std::string _pattern;
        std::vector<FormatItem::ptr> _items;
    };
}